/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file lin_common.c
 * @brief This file defines lin driver functions
 */

/*******Includes***************************************************************/
#include "lin_drv.h"

/*******Definitions************************************************************/

/*******Variables**************************************************************/

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : BIT
 *** Description   : Return bit B in byte A
 ******************************************************************************/
static inline uint8_t BIT(uint8_t A,
                          uint8_t B)
{
    return (uint8_t)((A >> B) & 0x01U);
}

/*******************************************************************************
 *** Function Name : LIN_ProcessParity
 *** Description   : Makes or checks parity bits
 ******************************************************************************/
uint8_t LIN_ProcessParity(uint8_t PID,
                          lin_parity_t typeAction)
{
    uint8_t parity;
    uint8_t retValue;

    parity = (uint8_t)(((0xFFU & (BIT(PID, 0U) ^ BIT(PID, 1U) ^ BIT(PID, 2U) ^ BIT(PID, 4U))) << 6U) |
                       ((0xFFU ^ (BIT(PID, 1U) ^ BIT(PID, 3U) ^ BIT(PID, 4U) ^ BIT(PID, 5U))) << 7U));

    /* Check if action is checking parity bits */
    if (LIN_CHECK_PARITY == typeAction) {
        /* If parity bits are incorrect */
        if ((PID & 0xC0U) != parity) {
            /* Return 0xFF if parity bits are incorrect */
            retValue = 0xFFU;
        }
        /* If parity bits are correct */
        else {
            /* Return ID if parity bits are correct */
            retValue = (uint8_t)(PID & 0x3FU);
        }
    }
    /* If action is making parity bits */
    else {
        /* Return PID in case of making parity bits */
        retValue = (uint8_t)(PID | parity);
    }

    return retValue;
}

/*******************************************************************************
 *** Function Name : LIN_CalcChecksum
 *** Description   : Makes the checksum byte for a frame
 ******************************************************************************/
uint8_t LIN_MakeChecksumByte(const uint8_t* buffer,
                             uint8_t dataLen,
                             uint8_t PID)
{
    uint8_t length;
    uint16_t checksum = 0U;

    /* For PID is 0x3C (ID 0x3C) or 0x7D (ID 0x3D) or 0xFE (ID 0x3E) or 0xBF (ID 0x3F)
     * apply classic checksum and apply enhanced checksum for other PID */
    if ((0x3CU != PID) && (0x7DU != PID) && (0xFEU != PID) && (0xBFU != PID)) {
        /* For PID other than 0x3C, 0x7D, 0xFE and 0xBF: Add PID in checksum calculation */
        checksum = PID;
    } else {
        /* For 0x3C, 0x7D, 0xFE and 0xBF: Do not add PID in checksum calculation */
        checksum = 0U;
    }

    for (length = dataLen; 0U < length; length--) {
        checksum += *buffer;
        buffer++;

        /* Deal with the carry */
        if (checksum > 0xFFU) {
            checksum -= 0xFFU;
        }
    }

    /* Return reversed checksum */
    return (uint8_t)(~checksum);
}

/*******EOF********************************************************************/
